<?php
/**
*
* ShopWindow Toolset
*
* Copyright (C) 2008 Digital Window Ltd.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*
*/


require_once('class.api_category.php');
require_once('class.api_category_tree.php');
require_once('class.api_category_path.php');
require_once('class.api_descendant_category_ids.php');
require_once('class.api_merchant_list.php');
require_once('class.api_merchant.php');
require_once('class.api_product.php');
require_once('class.api_product_list.php');
require_once('class.api_query_list.php');



/**
 * Base class for SW
 */
class shopcore
{
	var $iMerchantId;
	var $iCategoryId	= 0 ;
	var $iProductId;
	var $sQuery;

	var $aMerchantIds	= array();
	var $aCategoryIds	= array();
	var $aProductIds	= array();

	var $oMerchant;
	var $oCategory;
	var $oProduct;

	var $aPathCategories;

	var $iLimit			= 10;
	var $iOffset		= 0;
	var $sSort;

	var $oApiParams;

	var $aRefineByActiveVals 	= array();
	var $aAllowedCategoryIds 	= array();

	var $aBreadCrumb 	 		= array();
	var $aHomeBreadCrumb 		= array('sName'=>'Home', 'sUrl'=>P_INDEX);



	/**
	 * The Constructor
	 *
	 * @copyright	DigitalWindow
 	 * @author		Kostas Melas <kostas@digitalwindow.com>
 	 *
	 * @param 		void
	 *
	 * @return 		obj
	 */	
	function shopcore()
	{
		// create the api client objects
		$this->oApiCategory 		 	 = new api_category();
		$this->oApiCategoryTree 	 	 = new api_category_tree();
		$this->oApiCategoryPath 	 	 = new api_category_path();
		$this->oApiDescendantCategoryIds = new api_descendant_category_ids();
		$this->oApiMerchantList		 	 = new api_merchant_list();
		$this->oApiMerchant 		 	 = new api_merchant();
		$this->oApiProduct 			 	 = new api_product();
		$this->oApiProductList		 	 = new api_product_list();
		$this->oApiQueryList		 	 = new api_query_list();

		// api params
		$this->oApiParams 			 = new stdClass();

		// get descendant category ids
		if ( VERTICAL_CATEGORY_ID > 0 ) {
			$oParams 						= new stdClass();
			$oParams->iCategoryId 			= VERTICAL_CATEGORY_ID;
			
			$this->aAllowedCategoryIds[] 	= VERTICAL_CATEGORY_ID;
			
			$descendantCategoryIds = $this->oApiDescendantCategoryIds->getDescendantCategoryIds($oParams);
			if (is_array($descendantCategoryIds)) {
			$this->aAllowedCategoryIds 		= array_merge($this->aAllowedCategoryIds, 
														$this->oApiDescendantCategoryIds->getDescendantCategoryIds($oParams));
			}
			else {
			    $this->aAllowedCategoryIds 	= array();
			}
		}

		// collect env vars
		$this->gatherEnvData();

		// get basic API objects
		$this->_fetchApiObjects();
	}



	/**
	 * Gather data passed in query string
	 *
	 * @copyright	DigitalWindow
	 * @author		Kostas Melas <kostas@digitalwindow.com>
	 *
	 * @param 		void
	 *
	 * @return 		void
	 */
	function gatherEnvData()
	{
		# Manual price range
		$iMinPrice = intval($_POST['min_price']);
		$iMaxPrice = intval($_POST['max_price']);
		
		// add passed values as RBs
		if ( $iMinPrice < $iMaxPrice ) {
			$_GET['rb'] .= strlen($_GET['rb']) > 0 ? '|' : '';
			$_GET['rb'] .= '1-'.$iMinPrice.'_'.$iMaxPrice;
		}


		// digest RBs
		if ( strlen($_GET['rb']) > 0 ) {
			$sRbQuery = trim( strip_tags( urldecode($_GET['rb']) ) );
			// use += to preserve keys, as array_merge() reorders numerical keys
			$this->aRefineByActiveVals += $this->digestRefineByQuery( $sRbQuery );
		}

		// main
		$this->iCategoryId 	= !empty($this->aRefineByActiveVals[4]) ? intval( $this->aRefineByActiveVals[4] ) 		: null;
		$this->iMerchantId 	= !empty($this->aRefineByActiveVals[3]) ? intval( $this->aRefineByActiveVals[3] ) 		: null;
		$this->iProductId 	= is_numeric($_GET['p']) 				? intval($_GET['p']) 							: null;
		$this->sQuery 		= strlen($_GET['q']) > 0 				? trim( strip_tags( urldecode($_GET['q']) ) ) 	: null;


		// product list
		$this->iListOffset 	= is_numeric($_GET['iListOffset']) 	? intval($_GET['iListOffset']) 	: null;
		$this->iListLimit 	= is_numeric($_GET['iListLimit']) 	? intval($_GET['iListLimit']) 	: null;
		$this->sListSort 	= strlen($_GET['sListSort']) > 0	? trim($_GET['sListSort']) 		: null;



		# Verticals
		// overwrite if category outside vertical
		if ( VERTICAL_CATEGORY_ID > 0 && !in_array($this->iCategoryId, $this->aAllowedCategoryIds) ) {
			$this->iCategoryId 				= VERTICAL_CATEGORY_ID;
			$this->aRefineByActiveVals[4] 	= VERTICAL_CATEGORY_ID;
		}


		// assign to api params
		$this->oApiParams->iCategoryId	= $this->iCategoryId;
		$this->oApiParams->iMerchantId 	= $this->iMerchantId;
		$this->oApiParams->iProductId  	= $this->iProductId;
		$this->oApiParams->sQuery 		= $this->sQuery;
		
		$this->oApiParams->iLimit		= $this->iListLimit;
		$this->oApiParams->iOffset		= $this->iListOffset;
		$this->oApiParams->sSort		= $this->sListSort;
		
		
		// assign arrays
		$this->oApiParams->aCategoryIds = array($this->oApiParams->iCategoryId);
		$this->oApiParams->aMerchantIds = array($this->oApiParams->iMerchantId);
		$this->oApiParams->aProductIds 	= array($this->oApiParams->iProductId);
		
		$this->oApiParams->aActiveRefineByGroups = $this->createRefineByGroups($this->aRefineByActiveVals);
	}



	/**
	 * Synchronizes the API params between ShopCore and the passed params obj
	 * Params set in the passed obj have priority, the rest get values from $this
	 *
	 * @copyright	DigitalWindow
 	 * @author		Kostas Melas <kostas@digitalwindow.com>
 	 *
	 * @param 		object $oTarget the parameters object
	 *
	 * @return 		void
	 */
	function syncApiParams(&$oTarget)
	{
		// sanity check
		if (!is_object($oTarget)) {
			$oTarget = new stdClass();
		}


		// assign vars
		$oTarget->iCategoryId 	= isset($oTarget->iCategoryId) 	? $oTarget->iCategoryId	: $this->oApiParams->iCategoryId;
		$oTarget->iMerchantId 	= isset($oTarget->iMerchantId) 	? $oTarget->iMerchantId : $this->oApiParams->iMerchantId;
		$oTarget->iProductId  	= isset($oTarget->iProductId) 	? $oTarget->iProductId 	: $this->oApiParams->iProductId;
		$oTarget->sQuery 		= isset($oTarget->sQuery)  		? $oTarget->sQuery 		: $this->oApiParams->sQuery;

		$oTarget->iLimit		= isset($oTarget->iLimit)  	? $oTarget->iLimit 	: $this->oApiParams->iLimit;
		$oTarget->iOffset		= isset($oTarget->iOffset) 	? $oTarget->iOffset : $this->oApiParams->iOffset;
		$oTarget->sSort			= isset($oTarget->sSort)  	? $oTarget->sSort 	: $this->oApiParams->sSort;

		if ( !is_array($oTarget->aActiveRefineByGroups) ) {
			$oTarget->aActiveRefineByGroups = $this->oApiParams->aActiveRefineByGroups;
		}

		// assign arrays
		$oTarget->aCategoryIds 	= is_int($oTarget->iCategoryId) ? array($oTarget->iCategoryId) 	: $oTarget->aCategoryIds;
		$oTarget->aMerchantIds 	= is_int($oTarget->iMerchantId) ? array($oTarget->iMerchantId) 	: $oTarget->aMerchantIds;
		$oTarget->aProductIds 	= is_int($oTarget->iProductId) 	? array($oTarget->iProductId) 	: $oTarget->aProductIds;
	}



	/**
	 * Build the links for navigation within the site
	 *
	 * @copyright	DigitalWindow
	 * @author		Kostas Melas <kostas@digitalwindow.com>
	 *
	 * @param string $sPage				the destination page URL 
	 * @param int	 $iProductId	
	 * @param int	 $iMerchantId
	 * @param int	 $iCategoryId
	 * @param string $sQuery			the search query
	 * @param string $sRefineByCSV		any comma seperated RB values to be appended to the resulting RB string
	 * 
	 * @return string	the full URL along with all the querystring values
	 */
	function buildLink($sPage='', $iProductId='', $iMerchantId='', $iCategoryId='', $sQuery='', $sRefineByCSV='')
	{
		# Escape inputs
		$sPage 			= trim( strip_tags( urldecode($sPage) ) );
		$iProductId 	= intval($iProductId);
		$iMerchantId 	= intval($iMerchantId);
		$iCategoryId 	= intval($iCategoryId);
		$sQuery 		= trim( strip_tags( urldecode($sQuery) ) );
		$sRefineByCSV 	= trim( strip_tags( urldecode($sRefineByCSV) ) );


		# Gather params
		$aParams = array();

		if ( $iProductId > 0 ) {
			$aParams['p'] = $iProductId;
		}

		if ( strlen($sQuery) > 0 ) {
			$aParams['q'] = urlencode($sQuery);
		}


		// convert params to RBs
		$aRbs = array();

		if ( $iCategoryId > 0 ) {
			$aRbs[] = '4-'.$iCategoryId;
		}

		if ( $iMerchantId > 0 ) {
			$aRbs[] = '3-'.$iMerchantId;
		}

		if ( strlen($sRefineByCSV) > 0 ) {
			$aRbs[] = $sRefineByCSV;
		}


		# Add RBs to params
		$aParams['rb'] = $this->createRefineByQuery( $this->digestRefineByQuery(implode('|', $aRbs)) );


		# Add params to query
		// get the name of the executing page
		if ( empty($sPage) ) {
			$sScriptName = preg_replace('/\/.*\//', '', $_SERVER['SCRIPT_NAME']);
			$sScriptName = str_replace('/', '', $sScriptName ); // pop the slash if there's still one
			$sScriptName = HOME_URL.$sScriptName;
		}
		else {
			$sScriptName = $sPage;
		}


		$sReturn = $sScriptName;
		$i = 0;

		foreach ($aParams as $key => $val) {

			if ( empty($val) ) {
				continue;
			}

			if ($i == 0) {
				$sReturn .= '?';
			}
			else {
				$sReturn .= '&';
			}

			$sReturn .= $key.'='.$val;
			
			$i++;
		}

		return $sReturn;
	}



	/**
	* get the breadcrum in html form
	*
	* @access    public
	* @copyright affiliatewindow
	* @author    PAI <paul@affiliatewindow.com>
	* 
	* @param	boolean	bHtml	Display html
	* 
	* @return 	Mixed			false if no crumbs, else string html'ed output of $this->aBreadCrumb
	*/
	function getBreadCrumb($bHtml=true)
	{
		# if there are no crumbs, the loaf is complete
		if ( !array_diff($this->aBreadCrumb, $this->aHomeBreadCrumb) )
		{
			return false;
		}

		$iCrumbCount=0;
		$sReturn= '';

		foreach ( $this->aBreadCrumb as $aCrumb )
		{
			if ( strlen($aCrumb['sUrl'])>0 && $bHtml===true ) {
				$sReturn .= '<a href="'.$aCrumb['sUrl'].'">';
			}

			if ( $iCrumbCount==0 && $bHtml===false ) {
				$sReturn .= '';
			}
			else {
				$sReturn .= strlen($aCrumb['sName'])>40 ? substr($aCrumb['sName'], 0, 37).'...' : $aCrumb['sName'];
			}

			if ( strlen($aCrumb['sUrl'])>0 && $bHtml===true ) {
				$sReturn .= '</a>';
			}

			if ( $bHtml===true ) {
				$sReturn .= "\n";
			}

			$iCrumbCount++;

			if ( count($this->aBreadCrumb)!=$iCrumbCount ) {
				$sReturn .= ' > ';
			}
		}

		return $sReturn;
	}



	/**
	* build array of the breakcrumb
	*
	* @access    public
	* @copyright affiliatewindow
	* @author    PAI <paul@affiliatewindow.com>
	* @return    Array containing breadcrumb array ( 'sName', 'sUrl')
	*/
	function buildBreadCrumb()
	{
		$this->aBreadCrumb = array();
		$this->aBreadCrumb[] = $this->aHomeBreadCrumb;

		
		# Category
		if ( is_object($this->oCategory) ) {
			
				foreach ( $this->aPathCategories as $oCategory) {
					// restrict on verticals
					if ( VERTICAL_CATEGORY_ID > 0 && is_array($this->aAllowedCategoryIds) && !in_array($oCategory->iId, $this->aAllowedCategoryIds) ) {
						continue;
					}
					
					if (VERTICAL_CATEGORY_ID == $oCategory->iId) {
					    $this->aBreadCrumb[] = array( 'sName' => $oCategory->sName, 
												      'sUrl'  => $this->buildLink( P_CATEGORY, '', '', $oCategory->iId) );
					}
					else {
					    $this->aBreadCrumb[] = array( 'sName' => $oCategory->sName, 
												      'sUrl'  => $this->buildLink(( $oCategory->iParentId==0 ? P_CATEGORY : P_PRODUCTLIST ), '', '', $oCategory->iId) );
					}
				}

			if (!empty($this->oCategory->sName)) {
				$this->aBreadCrumb[] = array( 'sName' => $this->oCategory->sName, 
										  'sUrl'  => $this->buildLink( (count($this->aBreadCrumb) == 1 ? P_CATEGORY : P_PRODUCTLIST ), '', '', $this->oCategory->iId) );
			}
		}

		# Merchant
		if ( is_object($this->oMerchant) && !empty($this->oMerchant->sName) ) {
			$this->aBreadCrumb[] = array( 'sName' => $this->oMerchant->sName, 
							'sUrl' => $this->buildLink(P_PRODUCTLIST, '', $this->oMerchant->iId, $this->oCategory->iId) );
		}


		return $this->aBreadCrumb;
	}

	
	
	/**
	 * Fetches and loads the basic entities Category, Merchant, Product, etc. depending on available info
	 *
	 * @copyright	DigitalWindow
 	 * @author		Kostas Melas <kostas@digitalwindow.com>
 	 *
	 * @param 		void
	 *
	 * @return 		void
	 */
	function _fetchApiObjects()
	{   
		# get product
		if ( $this->oApiParams->iProductId > 0 ) {
			
			$aProducts = $this->oApiProduct->getProduct($this->oApiParams);
			$this->oProduct = $aProducts[$this->oApiParams->iProductId];
			
			// overwrite params to get the product's merchant & category
			$this->oApiParams->iMerchantId = $this->oProduct->iMerchantId;			
			$this->oApiParams->iCategoryId = $this->oProduct->iCategoryId;
		}		
		
		# get category & path & tree
		if ( $this->oApiParams->iCategoryId > 0 ) {
			
			$this->oApiParams->aCategoryIds = array($this->oApiParams->iCategoryId);
			
			$aCategories = $this->oApiCategory->getCategory($this->oApiParams);
			$this->oCategory = $aCategories[$this->oApiParams->iCategoryId];
			
			# path
			$this->aPathCategories = $this->oApiCategoryPath->getCategoryPath($this->oApiParams);
		}
			
		# get merchant
		if ( $this->oApiParams->iMerchantId > 0 ) {
			
			$this->oApiParams->aMerchantIds = array($this->oApiParams->iMerchantId);
			
			$aMerchants = $this->oApiMerchant->getMerchant($this->oApiParams);
			$this->oMerchant = $aMerchants[$this->oApiParams->iMerchantId];		
		}
	}
	

	
	/**
	 * Deduplicate and append refine by pair values to an array class var
	 *
	 * @copyright	DigitalWindow
	 * @author		Kostas Melas <kostas@digitalwindow.com>
	 *
	 * @param 		string  $sRefineByQuery the RBs GET query part
	 *
	 * @return 		array	the pairs as array with [defId]=>valId
	 */
	function digestRefineByQuery($sRefineByQuery)
	{
		if ( strlen($sRefineByQuery) < 1 ) {
			return array();
		}
		
		//e.g. rb=2-23,1-20
		$aPairs = explode('|', $sRefineByQuery);
		
		$aValues = array();

		foreach ($aPairs as $sPair) {
			$aParts = explode('-', $sPair);

			// remove definitions where value was 0
			if ($aParts[1]=='0') {
				unset($aValues[$aParts[0]]);
			}
			else {
				$aValues[$aParts[0]]= $aParts[1];
			}
		}

		
		return $aValues;
	}



	/**
	 * Creates the GET query string from the RB pairs array
	 *
	 * @copyright	DigitalWindow
	 * @author		Kostas Melas <kostas@digitalwindow.com>
	 *
	 * @param 		void
	 *
	 * @return 		string	the refine bys GET query 
	 */
	function createRefineByQuery($aRefineByVals)
	{
		$aValue = array();

		foreach ($aRefineByVals as $k=>$v) {
			// Basic valdation to check refine by isnt empty
			if ( !empty($k) && !empty($v) ) {
				$aValues[] = $k.'-'.$v;
			}
		}

		$sValues = @implode('|', $aValues);

		
		return $sValues;
	}
	


	/**
	 * Creates the active API RB data structure
	 *
	 * @copyright	DigitalWindow
	 * @author		Kostas Melas <kostas@digitalwindow.com>
	 *
	 * @param 		array	$aRefineByVals	the associative array of RB values
	 *
	 * @return 		array
	 */
	function createRefineByGroups($aRefineByVals)
	{
		$aActiveRefineByGroups = array();
		
		foreach ($aRefineByVals as $iGroupId=>$sVal) {
			
			// create definition first
			$oRefineByDefinition 		= new stdClass();
			$oRefineByDefinition->sId 	= $sVal;
			$oRefineByDefinition->sName = '';
			
			// add to group
			$oRefineByGroup 		= new stdClass();
			$oRefineByGroup->iId 	= $iGroupId;
			$oRefineByGroup->sName 	= '';
			$oRefineByGroup->aRefineByDefinitions = array($oRefineByDefinition);

			$aActiveRefineByGroups[] = $oRefineByGroup;
		}

		return $aActiveRefineByGroups;
	}

} // class

?>